package com.example.opengl.fbo

import android.graphics.SurfaceTexture
import android.opengl.GLES11
import android.opengl.GLES11Ext
import android.opengl.GLES20
import android.opengl.Matrix
import com.example.opengl.render.base.IDrawer
import retrofit2.http.OPTIONS
import java.nio.ByteBuffer
import java.nio.ByteOrder
import java.nio.FloatBuffer
import javax.microedition.khronos.opengles.GL

class FBOVideoDrawer : IDrawer {
    /**
     * 上下颠倒顶点矩阵
     */
    private val mReserveVertexCoors = floatArrayOf(
        -1f, 1f,
        1f, 1f,
        -1f, -1f,
        1f, -1f
    )

    private val mDefVertexCoors = floatArrayOf(
        -1f, -1f,
        1f, -1f,
        -1f, 1f,
        1f, 1f
    )

    /**
     * 顶点坐标
     */
    private var mVertexCoors = mDefVertexCoors

    /**
     * 纹理坐标
     */
    private val mTextureCoors = floatArrayOf(
        0f, 1f,
        1f, 1f,
        0f, 0f,
        1f, 0f
    )

    private var mWorldWidth: Int = -1
    private var mWorldHeight: Int = -1
    private var mVideoWidth = -1
    private var mVideoHeight = -1

    private var mTextureID = -1

    private var mSurfaceTexture: SurfaceTexture? = null
    private var mSftCb: ((SurfaceTexture) -> Unit)? = null

    /**
     * OpenGl程序id
     */
    private var mProgram = -1

    /**
     * 顶点矩阵变换接收者
     */
    private var mVerTexMatrixHandler = -1

    /**
     * 顶点坐标接收者
     */
    private var mVertexPosHandler = -1

    /**
     * 纹理坐标接收者
     */
    private var mTexturePosHandler = -1

    /**
     * 纹理接收者
     */
    private var mTextureHandler = -1

    /**
     * 透明度接收者
     */
    private var mAlphaHandler = -1

    /***灵魂出窍有关**/

    /**
     * 帧缓冲区
     */
    private var mSoulFrameBuffer: Int = -1

    /**
     * 纹理id
     */
    private var mSoulTextureId: Int = -1

    /**
     * 纹理接收者
     */
    private var mSoulTextureHandler: Int = -1

    /**
     * 缩放进度接收者
     */
    private var mProgressHandler: Int = -1

    /**
     * 是否更新FBO纹理
     */
    private var mDrawFbo: Int = 1

    /**
     * 更新FBO标记接收者
     */
    private var mDrawFobHandler: Int = -1

    /**
     * 一帧时间
     */
    private var mDModifyTime: Long = -1

    private lateinit var mVertexBuffer: FloatBuffer
    private lateinit var mTextureBuffer: FloatBuffer
    private var mMatrix: FloatArray? = null
    private var mAlpha = 1f

    init {
        initPos()
    }

    private fun initPos() {
        val bb = ByteBuffer.allocateDirect(mVertexCoors.size * 4)
        bb.order(ByteOrder.nativeOrder())
        mVertexBuffer = bb.asFloatBuffer()
        mVertexBuffer.put(mVertexCoors)
        mVertexBuffer.position(0)

        val cc = ByteBuffer.allocateDirect(mTextureCoors.size * 4)
        cc.order(ByteOrder.nativeOrder())
        mTextureBuffer = cc.asFloatBuffer()
        mTextureBuffer.put(mTextureCoors)
        mTextureBuffer.position(0)
    }


    override fun draw() {
        if (mTextureID != -1) {
            initDefMatrix()
            createGLPrg()
            updateFBO()
            activateSoulTexture()
            activateDefTexture()
            updateTexture()
            doDraw()
        }
    }

    private var mWidthRatio = 1f
    private var mHeightRatio = 1f
    private fun initDefMatrix() {
        if (mMatrix != null) return
        if (mVideoWidth != -1 && mVideoHeight != -1 && mWorldHeight != -1 && mWorldWidth != -1) {
            mMatrix = FloatArray(16)
            val prjMatrix = FloatArray(16)
            var originRatio = mVideoWidth / mVideoHeight.toFloat()
            val worldRatio = mWorldWidth / mWorldHeight.toFloat()
            if (mWorldWidth > mWorldHeight) {
                if (originRatio > worldRatio) {
                    mHeightRatio = originRatio / worldRatio
                    Matrix.orthoM(
                        prjMatrix, 0,
                        -mWidthRatio, mWidthRatio,
                        -mHeightRatio, mHeightRatio,
                        3f, 5f
                    )
                } else {// 原始比例小于窗口比例，缩放高度度会导致高度超出，因此，高度以窗口为准，缩放宽度
                    mWidthRatio = worldRatio / originRatio
                    Matrix.orthoM(
                        prjMatrix, 0,
                        -mWidthRatio, mWidthRatio,
                        -mHeightRatio, mHeightRatio,
                        3f, 5f
                    )
                }
            } else {
                if (originRatio > worldRatio) {
                    mHeightRatio = originRatio / worldRatio
                    Matrix.orthoM(
                        prjMatrix, 0,
                        -mWidthRatio, mWidthRatio,
                        -mHeightRatio, mHeightRatio,
                        3f, 5f
                    )
                } else {// 原始比例小于窗口比例，缩放高度会导致高度超出，因此，高度以窗口为准，缩放宽度
                    mWidthRatio = worldRatio / originRatio
                    Matrix.orthoM(
                        prjMatrix, 0,
                        -mWidthRatio, mWidthRatio,
                        -mHeightRatio, mHeightRatio,
                        3f, 5f
                    )
                }
            }
            /**
             * 设置相机位置
             */
            val viewMatrix = FloatArray(16)
            Matrix.setLookAtM(
                viewMatrix, 0, 0f, 0f, 5.0f, 0f, 0f, 0f,
                0f, 1.0f, 0f
            )
            Matrix.multiplyMM(mMatrix, 0, prjMatrix, 0, viewMatrix, 0)

        }

    }


    private fun createGLPrg() {
        if (mProgram == -1) {
            /**
             * 加载顶点着色器
             */
            val vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, getVertexShader())

            /**
             * 加载片段着色器
             */
            val fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, getFragmentShader())
            /**
             * 创建着色器程序
             */
            mProgram = GLES20.glCreateProgram()
            /**
             * 将顶点着色器绑定到着色器程序
             */
            GLES20.glAttachShader(mProgram, vertexShader)
            /**
             * 将片段着色器绑定到着色器程序
             */
            GLES20.glAttachShader(mProgram, fragmentShader)
            /**
             * 链接着色器程序
             */
            GLES20.glLinkProgram(mProgram)

            mVerTexMatrixHandler = GLES20.glGetUniformLocation(mProgram, "uMatrix")
            mVertexPosHandler = GLES20.glGetAttribLocation(mProgram, "aPosition")
            mTextureHandler = GLES20.glGetUniformLocation(mProgram, "uTexture")

            mTexturePosHandler = GLES20.glGetAttribLocation(mProgram, "aCoordinate")
            mAlphaHandler = GLES20.glGetAttribLocation(mProgram, "alpha")

            mSoulTextureHandler = GLES20.glGetUniformLocation(mProgram, "uSoulTexture")
            mProgressHandler = GLES20.glGetUniformLocation(mProgram, "progress")
            mDrawFobHandler = GLES20.glGetUniformLocation(mProgram, "drawFbo")
        }
        /**
         * 使用着色程序
         */
        GLES20.glUseProgram(mProgram)

    }

    override fun getSurfaceTexture(cb: (surfaceTexture: SurfaceTexture) -> Unit) {
        mSftCb = cb
    }

    private fun updateFBO() {
        if (mSoulTextureId == -1) {
            mSoulTextureId = OpenGLTools.createFBOTexture(mVideoWidth, mVideoHeight)
        }
        if (mSoulFrameBuffer == -1) {
            mSoulFrameBuffer = OpenGLTools.createFrameBuffer()
        }
        if (System.currentTimeMillis() - mDModifyTime > 500) {
            mDModifyTime = System.currentTimeMillis()
            //绑定FBO
            OpenGLTools.bindFBO(mSoulFrameBuffer, mSoulTextureId)
            //配置FBO窗口
            configFboViewPort()
            //激活默认的纹理
            activateDefTexture()
            //更新纹理
            updateTexture()
            //绘制到FBO
            doDraw()
            //解绑FBO
            OpenGLTools.unbindFBO()
            //恢复默认绘制窗口
            configDefViewPort()

        }
    }

    /**
     * 配置FBO窗口
     */

    private fun configFboViewPort() {
        mDrawFbo = 1
        //将变换矩阵恢复为单位矩阵
        Matrix.setIdentityM(mMatrix, 0)
        //设置颠倒的顶点坐标
        mVertexCoors = mReserveVertexCoors
        //重新初始化顶点坐标
        initPos()
        GLES20.glViewport(0, 0, mVideoWidth, mVideoHeight)
        GLES20.glClearColor(0.0f, 0.0f, 0.0f, 0.0f)
        GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT)

    }

    /**
     * 恢复默认显示窗口
     */
    private fun configDefViewPort() {
        mDrawFbo = 0
        mMatrix = null
        mVertexCoors = mDefVertexCoors
        initPos()
        initDefMatrix()
        GLES20.glViewport(0, 0, mWorldWidth, mWorldHeight)
    }

    private fun activateSoulTexture() {
        activateTexture(GLES11.GL_TEXTURE_2D, mSoulTextureId, 1, mSoulTextureHandler)
    }

    private fun activateDefTexture() {
        activateTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, mTextureID, 0, mTextureHandler)
    }

    private fun activateTexture(type: Int, mTextureID: Int, i: Int, mTextureHandler: Int) {
        GLES20.glActiveTexture(GLES20.GL_TEXTURE0 + i)
        GLES20.glBindTexture(type, mTextureID)
        GLES20.glUniform1i(mTextureHandler, i)
        GLES20.glTexParameterf(type, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR.toFloat())
        GLES20.glTexParameterf(type, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR.toFloat())
        GLES20.glTexParameteri(type, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE) //当纹理的s坐标超出[0,1]范围时，限定读取纹理的边缘
        GLES20.glTexParameteri(type, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE)
    }

    private fun updateTexture() {
        mSurfaceTexture?.updateTexImage()
    }

    private fun doDraw() {
        GLES20.glEnableVertexAttribArray(mVertexPosHandler)
        GLES20.glEnableVertexAttribArray(mTexturePosHandler)
        GLES20.glUniformMatrix4fv(mVerTexMatrixHandler,1,false,mMatrix,0)
        GLES20.glUniform1f(mProgressHandler,(System.currentTimeMillis()-mDModifyTime)/500f)
        GLES20.glUniform1i(mDrawFobHandler,mDrawFbo)

        GLES20.glVertexAttribPointer(mVertexPosHandler,2,GLES20.GL_FLOAT,false,0,mVertexBuffer)
        GLES20.glVertexAttribPointer(mTexturePosHandler,2,GLES20.GL_FLOAT,false,0,mTextureBuffer)
        GLES20.glVertexAttrib1f(mAlphaHandler, mAlpha)
        GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP,0,4)
    }

    override fun setTextureID(id: Int) {
        mTextureID = id
        mSurfaceTexture = SurfaceTexture(id)
        mSftCb?.invoke(mSurfaceTexture!!)
    }

    override fun release() {
        releaseFBO()
        GLES20.glDisableVertexAttribArray(mVertexPosHandler)
        GLES20.glDisableVertexAttribArray(mTexturePosHandler)
        GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0)
        GLES20.glDeleteTextures(1, intArrayOf(mTextureID), 0)
        GLES20.glDeleteProgram(mProgram)
    }
    private fun releaseFBO() {
        val fbs = IntArray(1)
        fbs[0] = mSoulFrameBuffer
        val texts = IntArray(1)
        texts[0] = mSoulTextureId

        OpenGLTools.deleteFBO(fbs, texts)
    }
    override fun setWorldSize(worldW: Int, worldH: Int) {
        mWorldHeight = worldH
        mWorldWidth = worldW
    }

    override fun setVideoSize(videoW: Int, videoH: Int) {
        mVideoHeight = videoH
        mVideoWidth = videoW
    }

    private fun loadShader(type: Int, shaderCode: String): Int {
        //根据type创建顶点着色器或者片元着色器
        val shader = GLES20.glCreateShader(type)
        //将资源加入到着色器中，并编译
        GLES20.glShaderSource(shader, shaderCode)
        GLES20.glCompileShader(shader)

        return shader
    }

    private fun getVertexShader(): String {
        return "attribute vec4 aPosition;" +
                "precision mediump float;" +
                "uniform mat4 uMatrix;" +
                "attribute vec2 aCoordinate;" +
                "varying vec2 vCoordinate;" +
                "attribute float alpha;" +
                "varying float inAlpha;" +
                "void main() {" +
                "    gl_Position = uMatrix*aPosition;" +
                "    vCoordinate = aCoordinate;" +
                "    inAlpha = alpha;" +
                "}"
    }

    private fun getFragmentShader(): String {
        //一定要加换行"\n"，否则会和下一行的precision混在一起，导致编译出错
        return "#extension GL_OES_EGL_image_external : require\n" +
                "precision mediump float;" +
                "varying vec2 vCoordinate;" +
                "varying float inAlpha;" +
                "uniform samplerExternalOES uTexture;" +
                "uniform float progress;" +
                "uniform int drawFbo;" +
                "uniform sampler2D uSoulTexture;" +
                "void main() {" +
                // 透明度[0,0.4]
                "float alpha = 0.6 * (1.0 - progress);" +
                // 缩放比例[1.0,1.8]
                "float scale = 1.0 + (1.5 - 1.0) * progress;" +

                // 放大纹理坐标
                // 根据放大比例，得到放大纹理坐标 [0,0],[0,1],[1,1],[1,0]
                "float soulX = 0.5 + (vCoordinate.x - 0.5) / scale;\n" +
                "float soulY = 0.5 + (vCoordinate.y - 0.5) / scale;\n" +
                "vec2 soulTextureCoords = vec2(soulX, soulY);" +
                // 获取对应放大纹理坐标下的纹素(颜色值rgba)
                "vec4 soulMask = texture2D(uSoulTexture, soulTextureCoords);" +

                "vec4 color = texture2D(uTexture, vCoordinate);" +

                "if (drawFbo == 0) {" +
                // 颜色混合 默认颜色混合方程式 = mask * (1.0-alpha) + weakMask * alpha
                "    gl_FragColor = color * (1.0 - alpha) + soulMask * alpha;" +
                "} else {" +
                "   gl_FragColor = vec4(color.r, color.g, color.b, inAlpha);" +
                "}" +
                "}"
    }

}